#!/usr/bin/env python
# -*- coding:utf-8 -*-

# import
import sys
import os
import socket
import re
import fcntl
import struct
import json
import time
import datetime
import threading
import subprocess
import hashlib
import base64
import glob
import urlparse
import urllib
import urllib2

# common
from common.log import log
from common.db.db import Db
from common.const import Const
from common.util import Util

# module

defaultencoding = 'utf-8'
if sys.getdefaultencoding() != defaultencoding:
    reload(sys)
    sys.setdefaultencoding(defaultencoding)


class DeviceInfo(object):
    '''
    设备信息结构体
    '''
    class Struct(object):
        def __init__(self, device_id, name, ip, device_type, parent, time):
            self.device_id = device_id
            self.name = name
            self.ip = ip
            self.device_type = device_type
            self.parent = parent
            self.time = time
            self.online = False

    def make_struct(self, device_id, name, ip, device_type, parent, time):
        return self.Struct(device_id, name, ip, device_type, parent, time)


class Device:
    '''
    设备管理
    '''

    def __init__(self):
        '''初始化'''
        log.debug('Device init')

        # 系统变量获取
        env = os.environ
        self.local_address = '127.0.0.1'
        self.srs_rtmp_port = '1935'
        self.srs_http_port = '8102'
        self.nginx_http_port = '8106'
        self.vis_root_path = '/opt/local/vis'
        self.vis_file_root_path = '/mnt/media/vis'
        try:
            self.srs_rtmp_port = env['SRS_RTMP_PORT']
        except Exception, e:
            pass
        try:
            self.srs_http_port = env['SRS_HTTP_PORT']
        except Exception, e:
            pass
        try:
            self.nginx_http_port = env['NGINX_HTTP_PORT']
        except Exception, e:
            pass
        try:
            self.vis_root_path = env['VIS_ROOT_PATH']
        except Exception, e:
            pass
        try:
            self.vis_file_root_path = env['VIS_FILE_ROOT_PATH']
        except Exception, e:
            pass

        # 所有设备
        self.all_devices = {}  # device_id : device_info

        # 激活设备
        self.active_devices = {}  # device_id : device_info

        # 拉流线程
        self.pull_rtsp_threads = {}  # device_id : thread
        self.pull_rtsp_subprocesses = {}  # device_id : subprocess

        self.preview_pull_rtsp_thread = None
        self.preview_pull_rtsp_subprocess = None

        # 设备信息结构体
        self.deviceInfo = DeviceInfo()

        log.debug('local_address:%s, srs_rtmp_port:%s, '
                  'srs_http_port:%s, vis_root_path:%s' %
                  (self.local_address, self.srs_rtmp_port, self.srs_http_port,
                   self.vis_root_path))

        # 加载设备数据
        self.load_devices()

        # 启动截图线程
        self.thumbnail_th = threading.Thread(target=self.thumbnail_process)
        self.thumbnail_th.start()

        # 启动在线监测线程
        self.ping_th = threading.Thread(target=self.ping_process)
        self.ping_th.start()

    def thumbnail_process(self):
        '''截图线程'''
        log.debug('thumbnail_process start...')

        thumbnail_size = Util.get_config_value('THUMBNAIL_SIZE')
        thumbnail_size_list = thumbnail_size.split('x')
        while True:
            for device_id, device_info in self.all_devices.items():
                rtsp_url = 'rtsp://%s/%s' % (device_info.ip,
                                             Const.HIGH_QUALITY_INDEX)
                thumbnail_cmd = ('%s/manager/etc/ffmpeg/bin/ffmpeg -v 0 -i %s '
                                 '-y -f image2 -ss 0.001 -frames:v 1 -s %sx%s '
                                 '%s/thumbnail/bak/%d.png &') % (
                    self.vis_root_path, rtsp_url, thumbnail_size_list[0],
                    thumbnail_size_list[1], self.vis_file_root_path,
                    device_id)
                os.system(thumbnail_cmd)

            time.sleep(10)
            mv_thumbnail_file_cmd = ('cd %s/thumbnail;mv -f bak/*.png .') % (
                self.vis_file_root_path)
            os.system(mv_thumbnail_file_cmd)

    def ping_process(self):
        '''在线监测线程'''
        log.debug('ping_process start...')

        while True:
            for device_id, device_info in self.all_devices.items():
                exit_code = os.system(
                    'ping -c 1 -q -w 1 %s >/dev/null' % device_info.ip)
                if exit_code:
                    device_info.online = False
                else:
                    device_info.online = True

            time.sleep(10)

    def load_devices(self):
        # 加载设备数据
        # 所有设备
        sql_ret = Db.execute('select DEVICE_ID,NAME,IP,TYPE,PARENT,TIME '
                             'from T_DEVICE;')
        for i in range(0, len(sql_ret)):
            tmp_device_id = int(sql_ret[i][0])
            tmp_name = str(sql_ret[i][1])
            tmp_ip = str(sql_ret[i][2])
            tmp_type = int(sql_ret[i][3])
            tmp_parent = int(sql_ret[i][4])
            tmp_time = str(sql_ret[i][5])

            if tmp_type == Const.DEVICE_TYPE_CAMERA:
                tmp_device = self.deviceInfo.make_struct(
                    tmp_device_id, tmp_name, tmp_ip, tmp_type, tmp_parent,
                    tmp_time)
                self.all_devices[tmp_device_id] = tmp_device

        # 激活设备
        sql_ret = Db.execute('select SUBSCREEN from T_STAGE;')
        if len(sql_ret) > 0:
            subscreen = str(sql_ret[0][0])
            subscreen_list = subscreen.split(',')
            self.active_devices.clear()
            for tmp_device_id in subscreen_list:
                device_id = int(tmp_device_id)
                if device_id != -1 and device_id in self.all_devices:
                    self.active_devices[device_id] = self.all_devices[
                        device_id]

        self.reset_all_pull_rtsp()

    def reset_all_pull_rtsp(self):
        # 结束旧的拉流线程
        to_delete_devices = self.pull_rtsp_threads.keys()
        for device_id in to_delete_devices:
            self.stop_pull_rtsp(device_id)

        # 启动新的拉流线程
        to_add_devices = self.active_devices.keys()
        for device_id in to_add_devices:
            self.start_pull_rtsp(device_id)

    def clear_all_pull_rtsp(self):
        '''停止所有拉流'''
        log.debug('clear_all_pull_rtsp')

        ret_code = 1000
        ret_data = None

        self.active_devices.clear()
        self.reset_all_pull_rtsp()

        return ret_code, ret_data

    def start_pull_rtsp(self, device_id):
        '''开始指定拉流'''
        log.debug(('start_pull_rtsp, device_id:%d') % (device_id))

        ret_code = 1000
        ret_data = None

        pull_rtsp_th = threading.Thread(target=self.pull_rtsp_process,
                                        args=(device_id, ))
        pull_rtsp_th.start()
        self.pull_rtsp_threads[device_id] = pull_rtsp_th

        return ret_code, ret_data

    def stop_pull_rtsp(self, device_id):
        '''停止指定拉流'''
        log.debug(('stop_pull_rtsp, device_id:%d') % (device_id))

        ret_code = 1000
        ret_data = None

        Util.stop_thread(self.pull_rtsp_threads[device_id])
        del self.pull_rtsp_threads[device_id]

        try:
            self.pull_rtsp_subprocesses[device_id].kill()
            del self.pull_rtsp_subprocesses[device_id]
        except Exception as e:
            pass

        return ret_code, ret_data

    def pull_rtsp_process(self, device_id):
        '''拉流线程'''
        log.debug('pull_rtsp_process, device_id:%d' % device_id)

        split = 16
        sql_ret = Db.execute('select SPLIT from T_STAGE;')
        if len(sql_ret) > 0:
            split = int(sql_ret[0][0])
        rtsp_quality_index = (
            Const.HIGH_QUALITY_INDEX if split == 1 else Const.LOW_QUALITY_INDEX
        )

        device_info = self.all_devices[device_id]
        rtsp_url = 'rtsp://%s/%s' % (device_info.ip, rtsp_quality_index)
        pull_rtsp_cmd = ('%s/manager/etc/ffmpeg/bin/ffmpeg -v 0 -i %s '
                         '-vcodec copy -acodec copy -f flv '
                         'rtmp://127.0.0.1:%s/live/%d') % (
            self.vis_root_path, rtsp_url, self.srs_rtmp_port, device_id)

        while True:
            log.debug('pull_rtsp_cmd:%s' % (pull_rtsp_cmd))
            p = subprocess.Popen(pull_rtsp_cmd, shell=True)
            self.pull_rtsp_subprocesses[device_id] = p
            p.wait()

            time.sleep(10)

    def query_devices(self):
        '''查询设备信息'''
        log.debug('query_devices')

        ret_code = 1000
        ret_data = None

        device_list = []
        sql_ret = Db.execute(
            'select DEVICE_ID,NAME,IP,TYPE,PARENT,TIME from T_DEVICE '
            'order by TIME;')
        for i in range(0, len(sql_ret)):
            tmp_device_id = int(sql_ret[i][0])
            tmp_name = str(sql_ret[i][1])
            tmp_ip = str(sql_ret[i][2])
            tmp_type = int(sql_ret[i][3])
            tmp_parent = int(sql_ret[i][4])
            tmp_time = str(sql_ret[i][5])
            tmp_online = 0
            tmp_live = ''
            tmp_icon = 'el-icon-menu'
            if tmp_type == Const.DEVICE_TYPE_CAMERA:
                tmp_online = self.all_devices[tmp_device_id].online
                tmp_live = 'http://%s:%s/live/%d.flv' % (
                    self.local_address, self.srs_http_port, tmp_device_id)
                if tmp_online:
                    tmp_icon = 'el-icon-video-camera'
                else:
                    tmp_icon = 'el-icon-video-camera'

            tmp_device_info = ('{"device_id":%d,"name":"%s","ip":"%s",'
                               '"type":%d,"parent":%d,"time":"%s","online":%d,'
                               '"live":"%s","icon":"%s"}') % (
                                   tmp_device_id, tmp_name, tmp_ip, tmp_type,
                                   tmp_parent, tmp_time, tmp_online, tmp_live,
                                   tmp_icon)
            device_list.append(json.loads(tmp_device_info))

        ret_data = json.dumps(Util.list_to_tree(device_list, 0, 'parent',
                                                'device_id'),
                              ensure_ascii=False)

        return ret_code, ret_data

    def query_cameras(self):
        '''查询相机信息'''
        log.debug('query_cameras')

        ret_code = 1000
        ret_data = None

        device_list = []
        sql_ret = Db.execute('select DEVICE_ID,NAME,IP,TYPE,PARENT,TIME '
                             'from T_DEVICE order by TIME;')
        for i in range(0, len(sql_ret)):
            tmp_device_id = int(sql_ret[i][0])
            tmp_name = str(sql_ret[i][1])
            tmp_ip = str(sql_ret[i][2])
            tmp_type = int(sql_ret[i][3])
            tmp_parent = int(sql_ret[i][4])
            tmp_time = str(sql_ret[i][5])
            tmp_online = 0
            if tmp_device_id in self.all_devices:
                tmp_online = self.all_devices[tmp_device_id].online
            tmp_thumbnail = 'http://%s:%s/thumbnail/%d.png' % (
                self.local_address, self.nginx_http_port, tmp_device_id)
            tmp_live = 'http://%s:%s/live/%d.flv' % (
                self.local_address, self.srs_http_port, tmp_device_id)
            tmp_preview_live = 'http://%s:%s/live/%d_preview.flv' % (
                self.local_address, self.srs_http_port, tmp_device_id)

            thumbnail_file_path = '%s/thumbnail/%d.png' % (
                self.vis_file_root_path, tmp_device_id)
            if not os.path.exists(thumbnail_file_path):
                prepare_thumbnail_file_cmd = ('cd %s/thumbnail;'
                                              'cp default.png %d.png') % (
                    self.vis_file_root_path, tmp_device_id)
                os.system(prepare_thumbnail_file_cmd)

            tmp_device_info = ('{"device_id":%d,"name":"%s","ip":"%s",'
                               '"type":%d,"parent":%d,"time":"%s","online":%d,'
                               '"thumbnail":"%s","live":"%s",'
                               '"preview":"%s"}') % (
                                   tmp_device_id, tmp_name, tmp_ip, tmp_type,
                                   tmp_parent, tmp_time, tmp_online,
                                   tmp_thumbnail, tmp_live, tmp_preview_live)
            device_list.append(json.loads(tmp_device_info))

        tree_list = Util.list_to_tree(device_list, 0, 'parent', 'device_id')
        final_device_list = []
        self.load_layer_devices(tree_list, final_device_list)

        ret_data = json.dumps(final_device_list, ensure_ascii=False)

        return ret_code, ret_data

    def load_layer_devices(self, parent_devices, device_list):
        if len(parent_devices) == 0:
            return
        for device in parent_devices:
            children = device['children']
            self.load_layer_devices(children, device_list)
            if device['type'] == Const.DEVICE_TYPE_CAMERA:
                device_list.append(device)

    def create_device(self, device_name, device_ip, device_type,
                      device_parent):
        '''创建设备'''
        log.debug('create_device, device_name:%s, device_ip:%s, '
                  'device_type:%d, device_parent:%d' %
                  (device_name, device_ip, device_type, device_parent))

        ret_code = 1000
        ret_data = None

        # 存储数据
        create_time = str(int(round(time.time() * 1000)))
        Db.execute(
            ('insert into T_DEVICE (NAME,IP,TYPE,PARENT,TIME) '
             'values ("%s", "%s", %d, %d, "%s");') %
            (device_name, device_ip, device_type, device_parent, create_time))

        if device_type == Const.DEVICE_TYPE_CAMERA:
            sql_ret = Db.execute('select max(DEVICE_ID) from T_DEVICE;')
            if len(sql_ret) > 0:
                device_id = int(sql_ret[0][0])
                tmp_device = self.deviceInfo.make_struct(
                    device_id, device_name, device_ip, device_type,
                    device_parent, create_time)
                self.all_devices[device_id] = tmp_device

        return ret_code, ret_data

    def delete_device(self, device_id):
        '''删除设备'''
        log.debug('delete_device, device_id:%d' % (device_id))

        ret_code = 1000
        ret_data = None

        children_num = 0
        sql_ret = Db.execute(
            'select count(*) from T_DEVICE where PARENT="%d";' % (device_id))
        if len(sql_ret) > 0:
            children_num = int(sql_ret[0][0])

        if device_id == -1:
            ret_code = 1009
            ret_data = '{"info": "invalid device id."}'
        elif children_num > 0:
            ret_code = 1010
            ret_data = '{"info": "has children, cannot delete."}'
        elif device_id in self.active_devices:
            ret_code = 1011
            ret_data = '{"info": "device is in using."}'
        else:
            # 删除数据
            Db.execute('delete from T_DEVICE where DEVICE_ID=%d;' %
                       (device_id))

            if device_id in self.all_devices:
                del self.all_devices[device_id]
            if device_id in self.active_devices:
                del self.active_devices[device_id]

        return ret_code, ret_data

    def active_camera(self, device_id):
        '''激活指定设备'''
        log.debug('active_camera, device_id:%d' % (device_id))

        return self.start_preview_pull_rtsp(device_id)

    def inactive_camera(self, device_id):
        '''取消激活指定设备'''
        log.debug('inactive_camera, device_id:%d' % (device_id))

        return self.stop_preview_pull_rtsp(device_id)

    def start_preview_pull_rtsp(self, device_id):
        '''开始设备预览拉流'''
        log.debug(('start_preview_pull_rtsp, device_id:%d') % (device_id))

        ret_code = 1000
        ret_data = None

        self.stop_preview_pull_rtsp(device_id)

        self.preview_pull_rtsp_thread = threading.Thread(
            target=self.preview_pull_rtsp_process,
            args=(device_id, ))
        self.preview_pull_rtsp_thread.start()

        return ret_code, ret_data

    def stop_preview_pull_rtsp(self, device_id):
        '''停止设备预览拉流'''
        log.debug(('stop_preview_pull_rtsp, device_id:%d') % (device_id))

        ret_code = 1000
        ret_data = None

        if self.preview_pull_rtsp_thread is not None:
            Util.stop_thread(self.preview_pull_rtsp_thread)
            self.preview_pull_rtsp_thread = None

        try:
            if self.preview_pull_rtsp_subprocess is not None:
                self.preview_pull_rtsp_subprocess.kill()
                self.preview_pull_rtsp_subprocess = None
        except Exception as e:
            pass

        return ret_code, ret_data

    def preview_pull_rtsp_process(self, device_id):
        '''预览拉流线程'''
        log.debug('preview_pull_rtsp_process, device_id:%d' % device_id)

        device_info = self.all_devices[device_id]
        rtsp_url = 'rtsp://%s/%s' % (device_info.ip, Const.HIGH_QUALITY_INDEX)
        preview_pull_rtsp_cmd = ('%s/manager/etc/ffmpeg/bin/ffmpeg -v 0 -i %s '
                                 '-vcodec copy -acodec copy -f flv '
                                 'rtmp://127.0.0.1:%s/live/%d_preview') % (
            self.vis_root_path, rtsp_url, self.srs_rtmp_port, device_id)

        while True:
            log.debug('preview_pull_rtsp_cmd:%s' % (preview_pull_rtsp_cmd))
            p = subprocess.Popen(preview_pull_rtsp_cmd, shell=True)
            self.preview_pull_rtsp_subprocess = p
            p.wait()

            time.sleep(10)

    '''
    以下为SRS回调函数
    '''

    def flv_finish(self, data):
        '''
        {"action":"on_dvr","client_id":113,"ip":"221.226.4.130",
        "vhost":"__defaultVhost__","app":"live","stream":"test9",
        "param":"","cwd":"/opt/local/vis/srs",
        "file":"/mnt/media/vis/flv/test9.1565141004202.flv"}
        '''
        return 0

    def on_connect(self, data):
        '''
        {"action":"on_connect","client_id":119,"ip":"221.226.4.130",
        "vhost":"__defaultVhost__","app":"live",
        "tcUrl":"rtmp://39.97.162.228/live","pageUrl":""}
        '''
        return 0

    def on_close(self, data):
        '''
        {"action":"on_close","client_id":119,"ip":"221.226.4.130",
        "vhost":"__defaultVhost__","send_bytes":4526,
        "recv_bytes":1595307,"app":"live"}
        '''
        return 0

    def on_publish(self, data):
        '''
        {"action":"on_publish","client_id":119,"ip":"221.226.4.130",
        "vhost":"__defaultVhost__","app":"live",
        "tcUrl":"rtmp://39.97.162.228/live","stream":"test9",
        "param":"?token=xxxx"}
        '''
        return 0

    def on_unpublish(self, data):
        '''
        {"action":"on_unpublish","client_id":119,"ip":"221.226.4.130",
        "vhost":"__defaultVhost__","app":"live","stream":"test9","param":""}
        '''
        datas = json.loads(data)
        if 'stream' in datas:
            device_id = datas['stream']

            # 如果还在窗口里，需要重新解码

        return 0

    def on_play(self, data):
        '''
        {"action":"on_play","client_id":644,"ip":"10.4.26.26",
        "vhost":"__defaultVhost__","app":"watch","stream":"test123",
        "param":"","pageUrl":""}
        '''
        return 0

    def on_stop(self, data):
        '''
        {"action":"on_stop","client_id":644,"ip":"10.4.26.26",
        "vhost":"__defaultVhost__","app":"watch","stream":"test123",
        "param":""}
        '''
        return 0

    def on_hls_notify(self, stream, ts_name):
        # log.debug('stream:%s, ts_name:%s' % (stream, ts_name))
        return 0
